useEffect – React 中文文档 您所在的位置:网站首页 为什么要用react hook useEffect – React 中文文档

useEffect – React 中文文档

2024-06-05 11:11| 来源: 网络整理| 查看: 265

现在你在 Effect 内部定义了 createOptions 函数,这样 Effect 本身只依赖于 roomId 字符串。通过此修复,输入框的输入不会重新连接聊天室。与被重新创建的函数不同,像 roomId 这样的字符串除非你将其设置为其它值,否则它不会改变。了解更多有关移除依赖项的信息。

从 Effect 读取最新的 props 和 state 正在建设中

本节描述了一个 实验性的 API,它还没有在一个稳定的 React 版本中发布。

默认情况下,在 Effect 中读取响应式值时,必须将其添加为依赖项。这样可以确保你的 Effect 对该值的每次更改都“作出响应”。对于大多数依赖项,这是你想要的行为。

然而,有时你想要从 Effect 中获取 最新的 props 和 state,而不“响应”它们。例如,假设你想记录每次页面访问时购物车中的商品数量:

function Page({ url, shoppingCart }) { useEffect(() => { logVisit(url, shoppingCart.length); }, [url, shoppingCart]); // ✅ 所有声明的依赖项 // ...}

如果你想在每次 url 更改后记录一次新的页面访问,而不是在 shoppingCart 更改后记录,该怎么办?你不能在不违反 响应规则 的情况下将 shoppingCart 从依赖项中移除。然而,你可以表达你 不希望 某些代码对更改做出“响应”,即使它是在 Effect 内部调用的。使用 useEffectEvent Hook 声明 Effect 事件,并将读取 shoppingCart 的代码移入其中:

function Page({ url, shoppingCart }) { const onVisit = useEffectEvent(visitedUrl => { logVisit(visitedUrl, shoppingCart.length) }); useEffect(() => { onVisit(url); }, [url]); // ✅ 所有声明的依赖项 // ...}

Effect 事件不是响应式的,必须始终省略其作为 Effect 的依赖项。这就是让你在其中放置非响应式代码(可以在其中读取某些 props 和 state 的最新值)的原因。通过在 onVisit 中读取 shoppingCart,确保了 shoppingCart 不会使 Effect 重新运行。

阅读更多关于 Effect Event 如何让你分离响应式和非响应式代码的内容。

在服务器和客户端上显示不同的内容

如果你的应用程序使用服务端(直接 或通过 框架)渲染,你的组件将会在两个不同的环境中渲染。在服务器上,它将渲染生成初始 HTML。在客户端,React 将再次运行渲染代码,以便将事件处理附加到该 HTML 上。这就是为什么要让 hydration 发挥作用,你的初始渲染输出必须在客户端和服务器上完全相同的原因。

在极少数情况下,你可能需要在客户端上显示不同的内容。例如,如果你的应用从 localStorage 中读取某些数据,服务器上肯定不可能做到这一点。以下是这如何实现的:

function MyComponent() { const [didMount, setDidMount] = useState(false); useEffect(() => { setDidMount(true); }, []); if (didMount) { // ... 返回仅客户端的 JSX ... } else { // ... 返回初始 JSX ... }}

当应用加载时,用户首先会看到初始渲染的输出。然后,当它加载完并进行 hydrate 时,Effect 将会运行并且将 didMount 设置为 true,从而触发重新渲染。这将切换到仅在客户端的渲染输出。Effect 不在服务器上运行,这就是为什么 didMount 在初始服务器渲染期间为 false 的原因。

谨慎使用此模式。请记住,网络连接速度较慢的用户将在相当长的时间内(可能是数秒钟)看到初始内容,因此你不希望对组件的外观进行突兀的更改。在许多情况下,你可以通过使用 CSS 条件性地显示不同的内容来避免这种需要。

疑难解答 Effect 在组件挂载时运行了两次

在开发环境下,如果开启严格模式,React 会在实际运行 setup 之前额外运行一次 setup 和 cleanup。

这是一个压力测试,用于验证 Effect 的逻辑是否正确实现。如果出现可见问题,则 cleanup 函数缺少某些逻辑。cleanup 函数应该停止或撤消 setup 函数所做的任何操作。一般来说,用户不应该能够区分 setup 被调用一次(如在生产环境中)和调用 setup → cleanup → setup 序列(如在开发环境中)。

阅读更多关于 这如何帮助找到 bug 和 如何修复你的逻辑。

Effect 在每次重新渲染后都运行

首先,请检查是否忘记指定依赖项数组:

useEffect(() => { // ...}); // 🚩 没有依赖项数组:每次重新渲染后重新运行!

如果你已经指定了依赖项数组,你的 Effect 仍循环地重新运行,那是因为你的某个依赖项在每次重新渲染时都是不同的。

你可以通过手动打印依赖项到控制台来调试此问题:

useEffect(() => { // .. }, [serverUrl, roomId]); console.log([serverUrl, roomId]);

然后,你可以右键单击控制台中来自不同重新渲染的数组,并都选择“存储为全局变量”。假设第一个被保存为 temp1,第二个被保存为 temp2,然后你可以使用浏览器控制台来检查两个数组中的每个依赖项是否相同:

Object.is(temp1[0], temp2[0]); // 第一个依赖项在数组之间是否相同?Object.is(temp1[1], temp2[1]); // 第二个依赖项在数组之间是否相同?Object.is(temp1[2], temp2[2]); // ... 以此类推检查每个依赖项 ...

当你发现某个依赖项在每次重新渲染都不同时,通常可以通过以下方式之一来解决:

在 Effect 中根据先前 state 更新 state 删除不必要的对象依赖项 删除不必要的函数依赖项 从 Effect 读取最新的 props 和 state

作为最后的手段(如果这些方法没有帮助),使用 useMemo 或 useCallback(用于函数)包装其创建。

Effect 函数一直在无限循环中运行

如果你的 Effect 函数一直在无限循环中运行,那么必须满足以下两个条件:

你的 Effect 函数更新了一些状态。 这些状态的改变导致了重新渲染,从而导致 Effect 函数依赖的状态发生改变。

在开始修复问题之前,问问自己,你的 Effect 是否连接到了某个外部系统(如 DOM、网络、第三方小部件等)。为什么你的 Effect 函数需要设置状态?它是否与外部系统同步?或者你正在试图用它来管理应用程序的数据流?

如果没有外部系统,请考虑 完全删除 Effect 函数 是否可以简化你的逻辑。

如果你真的正在与某个外部系统同步,请考虑为什么以及在何种条件下你的 Effect 函数应该更新状态。是否有任何变化会影响组件的可视输出?如果你需要跟踪一些不用于渲染的数据,使用一个 ref(它不会触发重新渲染)可能更合适。验证你的 Effect 函数不会超过需要地更新状态(并触发重新渲染)。

最后,如果你的 Effect 函数在正确的时机更新了状态,但仍然存在一个循环,那是因为该状态更新导致 Effect 的一个依赖项发生了更改。阅读如何调试依赖项变更。

即使组件没有卸载,cleanup 逻辑也会运行

cleanup 函数不仅在卸载期间运行,也在每个依赖项变更的重新渲染前运行。此外,在开发环境中,React 在组件挂载后会立即额外运行一次 setup + cleanup。

如果你的 cleanup 代码没有相应的 setup 代码,这通常是一种代码异味(code smell):

useEffect(() => { // 🔴 避免:cleanup 逻辑没有相应的 setup 逻辑 return () => { doSomething(); };}, []);

你的 cleanup 逻辑应该与 setup 逻辑“对称”,并且应该停止或撤销任何 setup 做的事情:

useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; }, [serverUrl, roomId]);

了解 Effect 生命周期与组件的生命周期有何不同。

我的 Effect 做了一些视觉相关的事情,在它运行之前我看到了一个闪烁

如果 Effect 一定要阻止浏览器绘制屏幕,使用 useLayoutEffect 替换 useEffect。请注意,绝大多数的 Effect 都不需要这样。只有当在浏览器绘制之前运行 Effect 非常重要的时候才需要如此:例如,在用户看到 tooltip 之前测量并定位它。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有